
package combat.dudes.client;

import gnu.cajo.utils.extra.TransparentItemProxy;

import java.awt.Polygon;
import java.util.ArrayList;

import combat.dudes.DudesFunctions;
import combat.dudes.client.gui.ClientGUI;
import combat.dudes.client.gui.ClientGUIInternalWindow;
import combat.dudes.client.gui.threads.ClientThread;
import combat.dudes.client.gui.threads.ClientThread.ClientThreadType;
import combat.dudes.client.gui.threads.ClientThreadStatus;
import combat.dudes.client.gui.threads.shell.GUIShell;
import combat.dudes.client.net.threads.NetSimulation;
import combat.dudes.protocol.data.ChatEntry;
import combat.dudes.protocol.interfaces.ChatInt;
import combat.dudes.protocol.interfaces.PhysicalObjectInt;
import combat.dudes.protocol.interfaces.ProjectileInt;
import combat.dudes.protocol.interfaces.ScenarioInt;
import combat.dudes.protocol.interfaces.SimulationInt;
import combat.dudes.protocol.interfaces.TeamInt;
import combat.dudes.protocol.interfaces.TrackInt;
import combat.dudes.protocol.interfaces.UserInt;
import combat.dudes.protocol.interfaces.VehicleInt;


/**
 * <strong>The client application of the D.U.D.E.S. Combat project</strong><br />
 * The client creates multiple threads: two main threads (NET and GUI) and each has its own subset of threads (shown below). The reason behind this is to not freeze the GUI when network-functionality
 * or -methods are being performed/called. The NET-threads retrieve information from the server and the GUI-threads will show this information to the user via a user interface (the client GUI).<br />
 * <ul>
 * <li>NET
 * <ul>
 * <li>NET_SIM</li>
 * <li>NET_CHAT</li>
 * </ul>
 * </li>
 * <li>GUI
 * <ul>
 * <li>GUI_SHELL</li>
 * <li>GUI_MAINMAP</li>
 * <li>GUI_MINIMAP</li>
 * <li>GUI_INFO</li>
 * <li>GUI_MYSTATUS</li>
 * <li>GUI_CHAT</li>
 * <li>CLOCK</li>
 * </ul>
 * </li>
 * </ul>
 * These threads are customized (override) and can be found in combat.dudes.client.gui.threads.ClientThread.java.
 * @author Laurens Andriessen
 * @version 1.0
 */

public class ClientApp {

	/**
	 * The start of the D.U.D.E.S. Combat Management System Client Application.
	 * @author Laurens Andriessen
	 * @param args
	 * @return void
	 */
	public static void main(String[] args) {

		// Output the status
		System.out.println(DudesFunctions.Status.getStatus());
		Log("D.U.D.E.S. Combat Client starting");
		Log("Client information:");
		Log("IP: " + DudesFunctions.Network.getMyIPAddress());

		// Creating and starting the NET and GUI threads
		ClientThread net = new ClientThread(ClientThreadType.NET);
		ClientThread gui = new ClientThread(ClientThreadType.GUI);
		net.start();
		gui.start();

		// Try to join the threads (in fact, they never will, because each thread ends in a never-ending while loop, so this ClientApp-thread stays open for as long as needed).
		try {
			net.join();
			gui.join();
		} catch(Exception e) {
			onError("Couldn't join threads.", e, true);
		}

		Log("Client done.");
	}


	/**
	 * The client-specific global variables, used to share information between NET- and GUI-threads
	 * @author Laurens Andriessen
	 */
	public static class SharedVars {

		/**
		 * Simulation cajo-binding
		 */
		public static SimulationInt sim = null;

		/**
		 * Scenario cajo-binding
		 */
		public static ScenarioInt scenario = null;

		/**
		 * Chat cajo-binding
		 */
		public static ChatInt chat = null;


		// Login
		private static Boolean isLoggedIn = false;
		public synchronized static Boolean getIsLoggedIn() {
			return isLoggedIn;
		}
		public synchronized static Boolean logIn(String username) {
			isLoggedIn = NetSimulation.doLogOn(username);
			return isLoggedIn;
		}


		// Server list
		private static ArrayList<String> serverlist = new ArrayList<String>();
		public synchronized static ArrayList<String> getServerlist() {
			return serverlist;
		}
		public synchronized static void addServer(String server) {
			serverlist.add(server);
		}


		// User properties
		// My user
		private static UserInt me = null;
		public synchronized static UserInt getMe() {
			return me;
		}
		public synchronized static void setMe(Object user) {
			me = (UserInt) TransparentItemProxy.getItem(user, new Class[] { UserInt.class });
		}
		// My vehicle
		private static VehicleInt myVehicle = null;
		public synchronized static VehicleInt getMyVehicle() {
			return myVehicle;
		}
		public synchronized static void setMyVehicle() {
			if(me != null) {
				myVehicle = (VehicleInt) TransparentItemProxy.getItem(me.getUserVehicle_Remote(), new Class[] { VehicleInt.class });
			}
			System.out.println(myVehicle.getClass());
		}
		// My team
		private static TeamInt myTeam = null;
		public synchronized static TeamInt getMyTeam() {
			return myTeam;
		}
		public synchronized static void setMyTeam() {
			if(me != null) {
				myTeam = (TeamInt) TransparentItemProxy.getItem(sim.findTeamOfUser_Remote(me), new Class[] { TeamInt.class });
			}
		}


		// Vehicles
		private static ArrayList<VehicleInt> vehicles = new ArrayList<VehicleInt>();
		public synchronized static ArrayList<VehicleInt> getVehicles() {
			return vehicles;
		}
		public synchronized static void setVehicles(ArrayList<VehicleInt> vehiclelist) {
			vehicles = vehiclelist;
		}
		public synchronized static void addVehicle(VehicleInt vehicle) {
			vehicles.add(vehicle);
		}

		// Projectiles
		private static ArrayList<ProjectileInt> projectiles = new ArrayList<ProjectileInt>();
		public synchronized static ArrayList<ProjectileInt> getProjectiles() {
			return projectiles;
		}
		public synchronized static void setProjectiles(ArrayList<ProjectileInt> projectilelist) {
			projectiles = projectilelist;
		}
		public synchronized static void addProjectile(ProjectileInt projectile) {
			projectiles.add(projectile);
		}

		// Tracks
		private static ArrayList<TrackInt> tracks = new ArrayList<TrackInt>();
		public synchronized static ArrayList<TrackInt> getTracks() {
			return tracks;
		}
		public synchronized static void setTracks(ArrayList<TrackInt> tracklist) {
			tracks = tracklist;
		}
		public synchronized static void addTrack(TrackInt track) {
			tracks.add(track);
		}


		// Chats
		private static ArrayList<ChatEntry> chats = new ArrayList<ChatEntry>();
		public synchronized static ArrayList<ChatEntry> getChats() {
			return chats;
		}
		public synchronized static void setChats(ArrayList<ChatEntry> chatlist) {
			chats = chatlist;
		}
		public synchronized static void addChat(ChatEntry chat) {
			chats.add(chat);
		}
		public synchronized static ArrayList<ChatEntry> getNewChats(int lastKnownMessageID) {
			ArrayList<ChatEntry> newChats = new ArrayList<ChatEntry>();
			for(ChatEntry msg : chats)
				if(msg.getMessageID() > lastKnownMessageID)
										newChats.add(msg);

			return newChats;
		}


		// Scenario land (optional)
		private static Polygon scenarioLand = new Polygon();
		public synchronized static Polygon getScenarioLand() {
			return scenarioLand;
		}
		public synchronized static void setScenarioLand(Polygon land) {
			scenarioLand = land;
		}


		private static PhysicalObjectInt selectedItemOnMap = null;
		public synchronized static PhysicalObjectInt getSelectedItemOnMap() {
			return selectedItemOnMap;
		}
		public synchronized static void setSelectedItemOnMap(PhysicalObjectInt item) {
			selectedItemOnMap = item;
		}


		/**
		 * Resets all variables and logs out on the server
		 * @author Laurens Andriessen
		 */
		public static void logOut() {

			try {
				for(ClientThread thread : ClientGUI.gui_threads_waiting) {
					if(thread.isAlive() && thread.getStatus() != ClientThreadStatus.WAITING) {
						thread.setStatus(ClientThreadStatus.WAITING);
						ClientApp.Log("Pauzing thread '" + thread.getName() + "'");
						thread.wait();
					}
				}

				for(ClientGUIInternalWindow window : GUIShell.windows) {
					if(window.isVisible())
						window.setVisible(false);
				}

				me = null;
				if(sim != null) {
					VehicleInt vehicle = (VehicleInt) TransparentItemProxy.getItem(getMe().getUserVehicle_Remote(), new Class[] { VehicleInt.class });
					if(vehicle != null) vehicle.unlock();
					getMe().setUserVehicle(null);
				}
				myTeam = null;
				myVehicle = null;

				vehicles = null;
				projectiles = null;
				tracks = null;

				sim = null;
				scenario = null;
				chat = null;
			} catch(Exception e) {}
		}

	}

	/**
	 * Alias for the setStatus()-method in DudesConfig.java
	 * @author Laurens Andriessen
	 */
	public static void Log(String status) {
		DudesFunctions.Status.setStatus(status);
	}

	/**
	 * Alias for the onError()-method in DudesConfig.java
	 * @author Laurens Andriessen
	 */
	public static void onError(String status, Exception e, Boolean doExit) {
		DudesFunctions.Status.onError(status, e, doExit);
	}
}
